package Snake;

import java.util.LinkedList;

import Game.Game;

public class Snake extends Thread {

	@SuppressWarnings("unused")
	private Cell cell;
	private int MAX_SNAKE_SIZE;
	private int id;
	private boolean running;
	private int x, y;
	private LinkedList<Cell> occupiedCells;
	private Game game;

	private boolean runMovemment;
	private boolean allowSnakeToMove;
	private boolean closedGUI;
	private boolean paint;

	private boolean runMovemmentRight;
	private boolean runMovemmentLeft;
	private boolean runMovemmentUp;
	private boolean runMovemmentDown;

	private int nextXRight;
	private int nextXLeft;
	private int nextYUp;
	private int nextYDown;

	private boolean hadDirections = false;
	private Cell SnakeDir = null;
	private boolean JumpPower = false;
	private boolean boostPower = false;
	private boolean boostRight = false;
	private boolean boostLeft = false;
	private boolean boostUp = false;
	private boolean boostDown = false;
	private int count = 0;

	public Snake(int id, Cell cell, Game game) {
		this.id = id;
		this.cell = cell;
		this.game = game;
		x = cell.getX();
		y = cell.getY();
		MAX_SNAKE_SIZE = 4;
		occupiedCells = new LinkedList<Cell>();
		running = true;
		closedGUI = false;
		paint = false;
	}

	public void run() {
		while (running) {
			try {
				Thread.sleep(800);
			} catch (InterruptedException e) {
				System.err.println("Thread.sleep Interrupted: "
						+ e.getMessage());
			}
			if (paint) {
				running = false;
			}
			move();
			if (checkWinnings()) {
				paint = true;
			}
			if (!running) {
				break;
			}
		}
		if (closedGUI) {
			occupiedCells.clear();
		}
		game.setStates();
	}

	private void putsInArray() {
		if (occupiedCells.size() <= MAX_SNAKE_SIZE) {
			occupiedCells.add(new Cell(x, y, id));
		}
	}

	private void removeLast() {
		if (occupiedCells.size() > MAX_SNAKE_SIZE) {
			for (int i = 0; i < game.getAllGameCells().size(); i++) {
				if (game.getAllGameCells().get(i).getX() == occupiedCells
						.getFirst().getX()
						&& game.getAllGameCells().get(i).getY() == occupiedCells
								.getFirst().getY()) {
					game.getAllGameCells().get(i).freeCell();
				}
			}
			occupiedCells.removeFirst();
		}
	}

	private void putsInHead() {
		for (int i = 0; i < occupiedCells.size(); i++) {
			if (occupiedCells.get(i) != occupiedCells.getLast()) {
				occupiedCells.get(i).setHead(false);
			} else {
				occupiedCells.get(i).setHead(true);
			}
		}
	}

	private void move() {
		if (boostPower) {
			for (int i = 0; i < 3; i++) {
				putsInArray();
				removeLast();
				putsInHead();
				if (running) {
					moveTheSnake();
				}
				
			}
		} else {
			putsInArray();
			removeLast();
			putsInHead();
			if (running) {
				moveTheSnake();
			}
		}
		game.setStates();
	}

	private boolean checkWinnings() {
		if (getSnakeId() == 1
				&& y == (game.getHeight() - game.getBlockSize()) - 28) {
			game.setWinningMessage(getSnakeId());
			return true;
		} else if (getSnakeId() == 2
				&& x == (game.getWidth() - game.getBlockSize()) - 6) {
			game.setWinningMessage(getSnakeId());
			return true;
		} else if (getSnakeId() == 3 && y == 0) {
			game.setWinningMessage(getSnakeId());
			return true;
		} else if (getSnakeId() == 4 && x == 0) {
			game.setWinningMessage(getSnakeId());
			return true;
		}
		return false;
	}

	private void moveToDest() {
		int random;
		int XDirS = SnakeDir.getX();
		int YDirS = SnakeDir.getY();
		if (x != XDirS / game.getBlockSize()
				&& y != YDirS / game.getBlockSize()) {
			if (!boostDown && !boostLeft && !boostRight && !boostUp) {
				if (x == XDirS && y > YDirS) {
					moveUp();
				} else if (x == XDirS && y < YDirS) {
					moveDown();
				} else if (x < XDirS && y == YDirS) {
					moveRight();
				} else if (x > XDirS && y == YDirS) {
					moveLeft();
				} else if (x > XDirS && y > YDirS) {
					random = (int) (Math.random() * 2);
					switch (random) {
					case 0:
						moveLeft();
						break;
					case 1:
						moveUp();
						break;
					}
				} else if (x > XDirS && y < YDirS) {
					random = (int) (Math.random() * 2);
					switch (random) {
					case 0:
						moveLeft();
						break;
					case 1:
						moveDown();
						break;
					}
				} else if (x < XDirS && y > YDirS) {
					random = (int) (Math.random() * 2);
					switch (random) {
					case 0:
						moveRight();
						break;
					case 1:
						moveUp();
						break;
					}
				} else if (x < XDirS && y < YDirS) {
					random = (int) (Math.random() * 2);
					switch (random) {
					case 0:
						moveRight();
						break;
					case 1:
						moveDown();
						break;
					}
				}
			} else {
				if (boostRight) {
					moveRight();
				} else if (boostLeft) {
					moveLeft();
				} else if (boostUp) {
					moveUp();
				} else if (boostDown) {
					moveDown();
				}
			}
		}
		if ((x / game.getBlockSize()) == XDirS / game.getBlockSize()
				&& (y / game.getBlockSize()) == YDirS / game.getBlockSize()) {
			hadDirections = false;
		}

	}

	public void moveTheSnake() {
		runMovemment = true;
		runMovemmentRight = true;
		runMovemmentLeft = true;
		runMovemmentUp = true;
		runMovemmentDown = true;
		allowSnakeToMove = false;
		nextXRight = x + game.getBlockSize();
		nextXLeft = x - game.getBlockSize();
		nextYUp = y - game.getBlockSize();
		nextYDown = y + game.getBlockSize();
		eatPower(x, y);
		while (runMovemment) {
			if (!hadDirections) {
				if (!boostDown && !boostLeft && !boostRight && !boostUp) {
					int random = (int) (Math.random() * 4);
					switch (random) {
					case 0:
						moveRight();
						break;
					case 1:
						moveLeft();
						break;
					case 2:
						moveUp();
						break;
					case 3:
						moveDown();
						break;
					default:
						break;
					}
				} else {
					if (boostRight) {
						moveRight();
					} else if (boostLeft) {
						moveLeft();
					} else if (boostUp) {
						moveUp();
					} else if (boostDown) {
						moveDown();
					}
				}

			} else {
				moveToDest();
			}
			if (noMoreMoves()) {
				running = false;
				break;
			}
			if (runMovemment == false) {
				runMovemment = true;
			} else if (allowSnakeToMove == true) {
				break;
			}
		}
	}

	private boolean noMoreMoves() {
		if (!runMovemmentRight && !runMovemmentLeft && !runMovemmentDown
				&& !runMovemmentUp) {
			return true;
		}
		return false;
	}

	private void moveRight() {
		if (checkBorders("RIGHT", boostPower)) {
			runMovemment = false;
			runMovemmentRight = false;
		} else {
			if (initialMovemment() && checkBarriers(nextXRight, y)) {
				if (boostPower) {
					if (!JumpPower) {
						getNextPosition(nextXRight, y).requestCell();
					}
					x = nextXRight;
				} else {
					if (!JumpPower) {
						getNextPosition(nextXRight, y).requestCell();
						if (getNextPosition(nextXRight, y).isToChangeDir()) {
							hadDirections = false;
						}
					}
					x = nextXRight;
				}
				allowSnakeToMove = true;
			} else {
				for (int i = 0; i < occupiedCells.size(); i++) {
					if ((checkBorders("RIGHT", boostPower)
							|| !checkBarriers(nextXRight, y)
							|| !checkBarriers(nextXRight + game.getBlockSize(),
									y)
							|| !checkBarriers(
									nextXRight + 2 * game.getBlockSize(), y) || (nextXRight == occupiedCells
							.get(i).getX() && y == occupiedCells.get(i).getY()))) {
						runMovemment = false;
						runMovemmentRight = false;
					}
				}
				if (runMovemment) {
					if (boostPower) {
						if (!JumpPower) {
							getNextPosition(nextXRight, y).requestCell();
						}
						boostRight = true;
						if (boostRight) {
							if (count == 3) {
								count = 0;
								boostRight = false;
							} else {
								count++;
							}
							allowSnakeToMove = true;
							x = nextXRight;
						}
					} else {
						if (!JumpPower) {
							getNextPosition(nextXRight, y).requestCell();
							if (getNextPosition(nextXRight, y).isToChangeDir()) {
								hadDirections = false;
							} else {
								x = nextXRight;
								allowSnakeToMove = true;
							}
						} else {
							x = nextXRight;
							allowSnakeToMove = true;
						}
					}
					if (JumpPower) {
						for (Cell c : game.getAllGameCells()) {
							if (c.getX() == x && c.getY() == y)
								c.setOccupied(true);
						}
					}
				}
			}
		}
	}

	private void moveLeft() {
		if (checkBorders("LEFT", boostPower)) {
			runMovemment = false;
			runMovemmentLeft = false;
		} else {
			if (initialMovemment() && checkBarriers(nextXLeft, y)) {
				if (!JumpPower) {
					getNextPosition(nextXLeft, y).requestCell();
				}
				x = nextXLeft;
				allowSnakeToMove = true;
			} else {
				for (int i = 0; i < occupiedCells.size(); i++) {
					if ((checkBorders("LEFT", boostPower)
							|| !checkBarriers(nextXLeft, y)
							|| !checkBarriers(nextXLeft - game.getBlockSize(),
									y)
							|| !checkBarriers(
									nextXLeft - 2 * game.getBlockSize(), y) || (nextXLeft == occupiedCells
							.get(i).getX() && y == occupiedCells.get(i).getY()))) {
						runMovemment = false;
						runMovemmentLeft = false;
					}
				}
				if (runMovemment) {
					if (boostPower) {
						if (!JumpPower) {
							getNextPosition(nextXLeft, y).requestCell();
						}
						boostLeft = true;
						if (boostLeft) {
							if (count == 3) {
								count = 0;
								boostLeft = false;
							} else {
								count++;
							}
							allowSnakeToMove = true;
							x = nextXLeft;
						}
					} else {
						if (!JumpPower) {
							getNextPosition(nextXLeft, y).requestCell();
							if (getNextPosition(nextXLeft, y).isToChangeDir()) {
								hadDirections = false;
							} else {
								x = nextXLeft;
								allowSnakeToMove = true;

							}
						} else {
							x = nextXLeft;
							allowSnakeToMove = true;
						}
					}
					if (JumpPower) {
						for (Cell c : game.getAllGameCells()) {
							if (c.getX() == x && c.getY() == y)
								c.setOccupied(true);
						}
					}
				}
			}
		}
	}

	private void moveUp() {
		if (checkBorders("UP", boostPower)) {
			runMovemment = false;
			runMovemmentUp = false;
		} else {
			if (initialMovemment() && checkBarriers(x, nextYUp)) {
				if (!JumpPower) {
					getNextPosition(x, nextYUp).requestCell();
				}
				y = nextYUp;
				allowSnakeToMove = true;
			} else {
				for (int i = 0; i < occupiedCells.size(); i++) {
					if ((checkBorders("UP", boostPower)
							|| !checkBarriers(x, nextYUp)
							|| !checkBarriers(x, nextYUp - game.getBlockSize())
							|| !checkBarriers(x,
									nextYUp - 2 * game.getBlockSize()) || (x == occupiedCells
							.get(i).getX() && nextYUp == occupiedCells.get(i)
							.getY()))) {
						runMovemment = false;
						runMovemmentUp = false;
					}
				}
				if (runMovemment) {
					if (boostPower) {
						if (!JumpPower) {
							getNextPosition(x, nextYUp).requestCell();
						}
						boostUp = true;
						if (boostUp) {
							if (count == 3) {
								count = 0;
								boostUp = false;
							} else {
								count++;
							}
							allowSnakeToMove = true;
							y = nextYUp;
						}
					} else {
						if (!JumpPower) {
							getNextPosition(x, nextYUp).requestCell();
							if (getNextPosition(x, nextYUp).isToChangeDir()) {
								hadDirections = false;
							} else {
								y = nextYUp;
								allowSnakeToMove = true;

							}
						} else {
							y = nextYUp;
							allowSnakeToMove = true;
						}
					}
					if (JumpPower) {
						for (Cell c : game.getAllGameCells()) {
							if (c.getX() == x && c.getY() == y)
								c.setOccupied(true);
						}
					}
				}
			}
		}
	}

	private void moveDown() {
		if (checkBorders("DOWN", boostPower)) {
			runMovemment = false;
			runMovemmentDown = false;
		} else {
			if (initialMovemment() && checkBarriers(x, nextYDown)) {
				if (!JumpPower) {
					getNextPosition(x, nextYDown).requestCell();
				}
				y = nextYDown;
				allowSnakeToMove = true;
			} else {
				for (int i = 0; i < occupiedCells.size(); i++) {
					if ((checkBorders("DOWN", boostPower)
							|| !checkBarriers(x, nextYDown)
							|| !checkBarriers(x,
									nextYDown + game.getBlockSize())
							|| !checkBarriers(x,
									nextYDown + 2 * game.getBlockSize()) || (x == occupiedCells
							.get(i).getX() && nextYDown == occupiedCells.get(i)
							.getY()))) {
						runMovemment = false;
						runMovemmentDown = false;
					}
				}
				if (runMovemment) {
					if (boostPower) {
						if (!JumpPower) {
							getNextPosition(x, nextYDown).requestCell();
						}
						boostDown = true;
						if (boostDown) {
							if (count == 3) {
								count = 0;
								boostDown = false;
							} else {
								count++;
							}
							allowSnakeToMove = true;
							y = nextYDown;
						}
					} else {
						if (!JumpPower) {
							getNextPosition(x, nextYDown).requestCell();
							if (getNextPosition(x, nextYDown).isToChangeDir()) {
								hadDirections = false;
							} else {
								y = nextYDown;
								allowSnakeToMove = true;
							}
						} else {
							y = nextYDown;
							allowSnakeToMove = true;
						}
					}
					if (JumpPower) {
						for (Cell c : game.getAllGameCells()) {
							if (c.getX() == x && c.getY() == y)
								c.setOccupied(true);
						}
					}
				}
			}
		}
	}

	private void eatPower(int xPower, int yPower) {
		for (int i = 0; i < game.getFoodPowers().size(); i++) {
			if (xPower == game.getFoodPowers().get(i).getCell().getX()
					&& yPower == game.getFoodPowers().get(i).getCell().getY()) {
				MAX_SNAKE_SIZE += 3;
				game.getFoodPowers().remove(i);
			}
		}
		for (int i = 0; i < game.getJumpPowers().size(); i++) {
			if (xPower == game.getJumpPowers().get(i).getCell().getX()
					&& yPower == game.getJumpPowers().get(i).getCell().getY()) {
				JumpPower = true;
				game.getJumpPowers().remove(i);
			}
		}
		for (int i = 0; i < game.getBoostPowers().size(); i++) {
			if (xPower == game.getBoostPowers().get(i).getCell().getX()
					&& yPower == game.getBoostPowers().get(i).getCell().getY()) {
				System.out.println("Apanhei Boost");
				boostPower = true;
				game.getBoostPowers().remove(i);
			}
		}
	}

	private boolean checkBarriers(int xBarrier, int yBarrier) {
		for (Barrier lol : game.getBarriers()) {
			if (xBarrier == lol.getCell().getX()
					&& yBarrier == lol.getCell().getY()) {
				return false;
			}
		}
		return true;
	}

	private boolean checkBorders(String toWhatBorder, boolean boost) {
		switch (toWhatBorder) {
		case "RIGHT":
			if (boost) {
				return nextXRight + 2 * game.getBlockSize() > game.getWidth()
						- game.getBlockSize();
			} else {
				return nextXRight > game.getWidth() - game.getBlockSize();
			}
		case "LEFT":
			if (boost) {
				return nextXLeft - 2 * game.getBlockSize() < 0;
			} else {
				return nextXLeft < 0;
			}
		case "UP":
			if (boost) {
				return nextYUp - 2 * game.getBlockSize() < 0;
			} else {
				return nextYUp < 0;
			}
		case "DOWN":
			if (boost) {
				return nextYDown + 2 * game.getBlockSize() > game.getHeight()
						- 2 * game.getBlockSize();
			} else {
				return nextYDown > game.getHeight() - 2 * game.getBlockSize();
			}
		default:
			break;
		}
		return false;
	}

	public Cell getNextPosition(int cellX, int cellY) {
		Cell cell2 = null;
		for (int i = 0; i < game.getAllGameCells().size(); i++) {
			if (game.getAllGameCells().get(i).getX() == cellX
					&& game.getAllGameCells().get(i).getY() == cellY) {
				cell2 = game.getAllGameCells().get(i);
			}
		}
		return cell2;
	}

	private boolean initialMovemment() {
		return occupiedCells.size() == 1;
	}

	public LinkedList<Cell> getOccupiedCells() {
		return occupiedCells;
	}

	public void setRunning(boolean running) {
		this.running = running;
	}

	public int getSnakeId() {
		return id;
	}

	public boolean isAllowSnakeToMove() {
		return allowSnakeToMove;
	}

	public void setClosedByGUI(boolean close) {
		this.closedGUI = close;
	}

	public void setDirections(Cell cell2) {
		hadDirections = true;
		SnakeDir = cell2;
	}

	public void interromper() {
		this.interrupt();
	}

	public boolean isJumpBoost() {
		return JumpPower;
	}

}
